// GLOBAL VARS
/*
 * Set of selected task objects in gui
 */ 
selected_tasks = [];
/*
 * Ordered list of tasks in visible list (not all tasks are 
 * always visible depends on filtering options)
 */
visible_tasks = [];
/*
 * A collection of tasks that were added/updated/deleted/completed in the latest 
 * state object update
 */
recent_delta = [];
/*
 * Helps determine if the task selected should be added to current selection (check box hit)
 * or be the only selected task (row hit)
 */
var is_cbox_hit = false;
/*
 * The view filter, controls which tasks are visible to user
 * Available Filters:
 * tagged: "personal",
 * contains: "mom",
 * due:"today/tomorrow",
 * is:"active/overdue/completed/recentDelta",
 */
var filters = { is:"active" };
var prevFilter = {};
/*
 * List sorting method = [ "name" | "priority" | "duedate" ]  
 */
sortBy = "priority"; // default

/*
 * This is the first function to be called after document is loaded
 */
function init_todoit_gui() {

	// Note: this event will be called before row.click event
		$('#row_template .cbox').click(function() {
			is_cbox_hit = true;
			var row = $(this).closest(".tsk_row")[0];// get tsk_row parent of
														// this checkbox
				// no longer using cb to select rows
				//toggleSelectTask(row.task);
			
				// checking a box will complete the task
				completeTasks([row.task]);
			});

		$('#row_template').click(function() {

			// if checkbox was hit, then do nothing here
			if (is_cbox_hit) {
				is_cbox_hit = false;
				return;
			}

			if ($.specialKeys('ctrl')) {
				toggleSelectTask(this.task);
			} else
				setSelectedTask(this.task);

		});

		// set hover style
		$('#row_template').hover(function() {
			$(this).addClass('tsk_row_hover');
			$(this).find('.cell_edit_icon').show();
			update_row_prio(this, this.task, true);
		}, function() {
			$(this).removeClass('tsk_row_hover');
			$(this).find('.cell_edit_icon').hide();
			update_row_prio(this, this.task, false);
		});

		initKBShortcuts();
};

function initKBShortcuts()
 {
	var shortcuts = {};

	// Focus add task field
	shortcuts["T"] = function() {
		$('#add-text').focus();
		return false;
	};

	// Escape - reset selection and add-task box
	$(document).bind('keydown', {
		combi : 'esc'
	}, function() {
		$('#add-text').val('');
		$('#add-text').blur();

		selected_tasks = [];
		gui_update_selections();
	});

	// Add random task
	//shortcuts["R"] = function() { randAction(1); };

	// Complete selected tasks
	shortcuts["C"] = function() {
		completeTasks(selected_tasks);
		selected_tasks = [];
		gui_update_selections();
	};
	// Select None of the tasks
	shortcuts["DEL"] = function() {
		deleteTasks(selected_tasks);
		selected_tasks = [];
		gui_update_selections();
	};
	// Select All tasks
	shortcuts["A"] = function() {
		selected_tasks = visible_tasks.slice(); // slice makes a copy of the array
		gui_update_selections();
	};
	// Select None of the tasks
	shortcuts["N"] = function() {
		selected_tasks = [];
		gui_update_selections();
	};

	// Show only overdue tasks
	shortcuts["O"] = function() {
		// TODO:...
	};

	setPrior = function(prio) {
		prioritizeTasks(selected_tasks, prio);
		selected_tasks = [];
		gui_update_selections();
	};
	shortcuts["1"] = function() {
		setPrior(1);
	};
	shortcuts["2"] = function() {
		setPrior(2);
	};
	shortcuts["3"] = function() {
		setPrior(3);
	};
	shortcuts["4"] = function() {
		setPrior(4);
	};

	var a = {
		s : "",
		sS : "aa"
	};

	// add shortcuts
	for ( var key in shortcuts)
		$(document).bind('keydown', {
			combi : key,
			disableInInput : true
		}, shortcuts[key]);

}


/*
 * Given a task, this function returns true/false weather the task pass the
 * selected filters (match the tags/date/keyowrds...)
 */
taskFilter = function (task) {
        
        var passFilter = true;
        
        var tag = filters["tagged"];
        var queryStr = filters["contains"];
        var dueDate = filters["due"];
        var state = filters["is"];
        
        // filter by tag
        if(tag)
                passFilter = passFilter && $.inArray(tag, task.tags) != -1;
                
        // filter by keyword
        if(queryStr){
                var keywords = queryStr.split(" ", 0);
                
                for ( var kw in keywords) 
                        passFilter = passFilter && (task.title.indexOf(kw) != -1);
        }
        
        //filter by due date
        if(dueDate){
                // TODO: add this
        }
        
        //filter by state
        if(state){
                if (state == "overdue") 
                        passFilter = passFilter && task.isOverdue();
                else if (state == "completed") 
                        passFilter = passFilter && task.isCompleted();
                else if (state == "active")
                        passFilter = passFilter && !task.isCompleted(); // by default, shows non-completed tasks
                else if (state == "recent")
                    passFilter = passFilter && recent_delta != null && indexOfTask(task, recent_delta) != -1;
        }
        
        return passFilter;
};

taskComparer = function(t1, t2) {

	var prioDelta = t1.priority - t2.priority;
	var dueDateDelta;

	if (t1.dueDate && t2.dueDate) { // both != null
		dueDateDelta = t2.dueDate.getDate() - t1.dueDate.getDate();
	} else if (!t1.dueDate && !t2.dueDate) { // both == null
		dueDateDelta = (prioDelta != 0) ? prioDelta : t1.id - t2.id; // last resort, by ids, to have a strong
	} else {
		dueDateDelta = (t1.dueDate == null) ? +1 : -1;
	}
	
	switch (sortBy) {
	case "name":
		return (t2.title[0] - t1.title[0]);
	case "priority":
		return (prioDelta != 0) ? prioDelta : dueDateDelta; // second sort:
															// dueDate
	case "duedate":
		return dueDateDelta;
	}
};

/*
 * Locate a task in array by id(!)
 */
function indexOfTask(task, taskArr) 
{
	for(var i = 0; i < taskArr.length; i++)
		if(taskArr[i].id == task.id)
			return i;
	
	return -1;
}


/*
 * Finds a tsk_row element that represents this task among the visible(!) task
 * rows
 */
function findVisibleRow(task)
{
        var idx = indexOfTask(task, visible_tasks);
        
        if(idx == -1) // not in view
                return null;
        
        // may have to use :not(:animated)
        return $(".tsk_list .tsk_row")[idx];
}

/*
 * Creates a task row matching the given task properties and returns the element
 */
function create_row(task)
{
        // Grab the template row and clone it ('true' for cloning with attached events like click/hover)
        var newRow = $('#row_template').clone(true)[0]; 
        
        // Update row contents
        gui_update_row(newRow, task);
        
        return newRow;
}


/* 
 * Gets a task row element and update its content (title, tags, ....) 
 * with given task properties. This method is used for both changing the 
 * default template when creating a new row and updating an existing task row.
 */
function gui_update_row(row, task) {
	// show completed tasks as (mmm dd) and all the rest show with relative
	// pretty date (today, yesterday, Sunday)
	var humanDate = "";
	
	// show completion date if available, otherwise due date (if available)
	if(task.isCompleted()) {
		humanDate = "Completed on " + task.completedDate.format("ddd, mmm dd, yyyy");
	} else if (task.dueDate) {
		humanDate = task.dueDate.format("ddd, mmm dd, yyyy");
	}
	// task.completedDate  ? task.dueDate.format("mmm dd") : prettyDate(task.dueDate);

	// update row
	row.task = task;
	row.id = task.id;
	update_row_prio(row, task, false);
	$(row).find('.cell_title').text(task.title);
	$(row).find('.cell_date').text(humanDate);

	// more (Second row)
	if (task.dueDate == null && task.linkedWave == null
			&& task.location == null && !task.isLinkedToWave()
			&& task.tags == null && task.url == null)
		$(row).find('.tsk_row_more').addClass("hidden");
	else
		$(row).find('.tsk_row_more').removeClass("hidden");

	// tags:
	if (task.tags && task.tags.length > 0) {
		var tagStr = task.tags[0];
		if (task.tags.length > 1)
			tagStr = accumulate(tagStr, task.tags.slice(1), function(acc, tag) {
				return acc + ", " + tag;
			});

		$(row).find('.cell_tags').text(tagStr);
	}

	// location
	var locationFld = $(row).find(".cell_location");
	if (task.location && task.location.length > 0) {
		locationFld[0].href = C_MAPS_QUERY_URL + task.location;
		locationFld.removeClass("hidden");
	} else
		locationFld.addClass("hidden");

	// url
	var linkFld = $(row).find(".cell_link");
	if (task.link && task.link.length > 0) {
		linkFld[0].href = task.link;
		linkFld.removeClass("hidden");
	} else
		linkFld.addClass("hidden");
	
	// discussion wave
	var linkedWaveFld = $(row).find(".cell_linkedWave");
	if (task.isLinkedToWave()) {
		linkedWaveFld[0].href = task.getLinkedWaveHref();
		linkedWaveFld.removeClass("hidden");
	} else {
		linkedWaveFld.addClass("hidden");
	}

	// set 'overdue' classes if necessary
	if (task.isOverdue()) { // not completed AND overdue
		$(row).addClass("tsk_row_overdue");
		$(row).find(".cell_title").addClass("cell_title_overdue");
	} else {
		$(row).removeClass("tsk_row_overdue");
		$(row).find(".cell_title").removeClass("cell_title_overdue");
	}
	
	// set completed classes if necessary
	if(task.isCompleted()) {
		$(row).find('.cell_title').addClass('line_througe_text');
		$(row).find('.cbox').attr("checked", true).attr("disabled", true);
	}else {
		$(row).find('.cbox').attr("checked", false).removeAttr("disabled");
	}
		
	
	// set participant icon
	if(isInsideWave()){
		var pImg = $(row).find('.cell_icon');
		var p = null;
		
		if (task.owner.length > 0) {
			var owner = wave.getParticipantById(task.owner);
			if (owner) p = owner;
		}
		if(!p) p = wave.getParticipantById(task.creator);
		
		pImg[0].src = p.getThumbnailUrl();
		pImg[0].alt = p.getDisplayName();
		pImg[0].title = p.getDisplayName();
	}
}

/*
 * Gets a task row element and updates its priority style (prio1 prio2 ...) if
 * ignoreCompleted is on, completed tasks don't get completed style (prioXc),
 * they get the usual (prioX) - used in hover Used in gui_update_row() and on
 * row:hover
 */
function update_row_prio(row, task, ignoreCompleted)
{
        var prio_class = "prio" + task.priority + ((!ignoreCompleted && task.completedDate) ? "c" : "");
        
        $(row).find('.cell_prio')
                  .removeClass("prio1 prio2 prio3 prio4 prio1c prio2c prio3c prio4c") // remove old style
                  .addClass(prio_class); // set new style
}

function setSortMethod(sortMethod) {
        sortBy = sortMethod;
        gui_reload_list();
}

function setFilter(newFilter) {
        filters = newFilter;
        gui_reload_list();
}

function showRecentDelta()
{
	if (filters != null && filters["is"] == "recent"){ // already showing recent delta
		setFilter(prevFilter);
	} else {
		prevFilter = filters;
		setFilter({is:'recent'});
	}
	
    gui_reload_list();
}

// public to core

function gui_update_callback(addedTasks, updatedTasks, completedTasks, deletedTasks)
{
        var humanActions = [];
        var changes = [];
        
        
        // Update gui according to state changes
        if (!nullOrEmpty(addedTasks)){
                humanActions.push("added " + humanCount("task", addedTasks.length));
                for ( var i = 0; i < addedTasks.length; i++)
                        gui_add_task(addedTasks[i], true);
                // Save the tasks related to the last state change
                changes = addedTasks;
        }
        
        if (!nullOrEmpty(updatedTasks)){
                humanActions.push("updated " + humanCount("task", updatedTasks.length));
                for ( var i = 0; i < updatedTasks.length; i++)
                        gui_update_task(updatedTasks[i], true);
                // Save the tasks related to the last state change
                changes = updatedTasks;
        }
        
        if (!nullOrEmpty(completedTasks)){
                humanActions.push("completed " + humanCount("task", completedTasks.length));
                for ( var i = 0; i < completedTasks.length; i++)
                        gui_remove_task(completedTasks[i], true, true);
                // Save the tasks related to the last state change
                changes = completedTasks;
        }
        
        if (!nullOrEmpty(deletedTasks)){
                humanActions.push("deleted " + humanCount("task", deletedTasks.length));
                for ( var i = 0; i < deletedTasks.length; i++)
                        gui_remove_task(deletedTasks[i], false, true);
                // Save the tasks related to the last state change
                changes = deletedTasks;
        }
        
       if(changes.length == 0) // TODO: BUG!
    	   return; 
       recent_delta = changes;
        
       // adjust gadget height
       // gadgets.window.adjustHeight();
       
        // build initiators array:
        /*var pplArr = accumulate([], recent_delta, function(acc, task) {
                var moder = task.lastModifier;
                
                // TODO: replace myself with wave api
                //if(moder == myself)
                //      moder = "You";
                
                if($.inArray(moder, acc) != -1)
                        return acc;
                        
                acc.push(moder);
                return acc;
        });*/

        
        // build initiators string: (p1, p2 and p3)
        var pplStr = recent_delta[0].lastModifier; //arrToHumanStr(pplArr);
        pplStr = participantNameById(pplStr);
        
        if(humanActions == null || humanActions.length == 0){
            alert("no actions");
        }
        
        // build actions string: (a1, a2 and a3)
        var actStr = arrToHumanStr(humanActions);
        
        // TODO: use timeago
        var statusLine = "<b>" + pplStr + "</b> " + actStr;
		$('#status_bar_text').html(statusLine);
}


function setSelectedTask(task)
{
        selected_tasks = [task];
        
        // explicitly tell gui to select task because this event
        // is not saved in state object
        gui_update_selections();
}

function toggleSelectTask(task)
{
        var idx = indexOfTask(task, selected_tasks);
        
        if(idx != -1){ // is selected?
                selected_tasks.splice(idx, 1); // remove selection
        }else{
                selected_tasks.push(task); // add selection
        }
        
        // explicitly tell gui to select task because this event
        // is not saved in state object
        gui_update_selections();
}

function toggleAddBoxFocus(isFocussed)
{
	
	var cli_fld = $('#cli');
	var lbl = "Write a new task here...";
	
	if(isFocussed){
		cli_fld.removeClass('label-input-label');
		if (cli_fld.val() == lbl) cli_fld.val("");
	} else {
		cli_fld.addClass('label-input-label');
		if (cli_fld.val().length < 1) cli_fld.val(lbl);
	}
		
}


/*
 * Updates the row styles 
 */
function gui_update_selections()
{
	/*if (selected_tasks.length == 0) { // clear all
		$('.tsk_list .tsk_row .cbox').attr('checked', false);
	}
	else */
	{
		$.each($(".tsk_list .tsk_row"), function(){
		        if(indexOfTask(this.task, selected_tasks) != -1) // is selected?
		                $(this).removeClass("tsk_row_hover") // temporarily remove hover style, so 'selected' style show
		                           .addClass("tsk_row_selected");
		                           //.find(".cbox").attr("checked", true); - cb no longer has to do with selecting
		                           
		        else
		                $(this).removeClass("tsk_row_selected"); //.find(".cbox").attr("checked", false);;
    });
  }
}

// TEMPFIX: (From Wikipedia default, supposedly stable)
function ms_merge(left,right,comparison) {
	var result = new Array();
	while((left.length > 0) && (right.length > 0))
		if(comparison(left[0],right[0]) <= 0) result.push(left.shift());
		else result.push(right.shift());
	while(left.length > 0) result.push(left.shift());
	while(right.length > 0) result.push(right.shift());
	return result;
}

function merge_sort(array,comparison) {
	if(array.length < 2) return array;
	var middle = Math.ceil(array.length/2);
	return ms_merge(merge_sort(array.slice(0,middle),comparison),
			merge_sort(array.slice(middle),comparison),
			comparison);
}


/*
 * Completely reloads the task list gui
 */
function gui_reload_list() {
    
    // clear selections
    selected_tasks = [];
    
    var list = $(".tsk_list");
    
    // filter
    visible_tasks = all_tasks.filter(taskFilter);
    // sort
    //visible_tasks = visible_tasks.sort(taskComparer);
    visible_tasks = merge_sort(visible_tasks, taskComparer); // TEMPFIX
    
    // Empty task list, except hidden template row
    list.empty();
    // rebuild list
    for ( var i = 0; i < visible_tasks.length; i++) 
    {
        var tsk = visible_tasks[i];
        var newRow = create_row(tsk);
        
        list.append(newRow);
        $(newRow).removeClass("hidden");
    }
    
    // adjust height (rows added/removed)
    adjustGadgetHeight();
}

function gui_update_task(updatedTask, isAnimated)
{
	// Check if task is visible in view
	if(indexOfTask(updatedTask, visible_tasks) == -1)
		return;
	
	// First reorder list in case sort order was damaged due to update 
	// option 1: gui_reload_list(); // not good
	// option 2: remove and insert the row again in place
	gui_remove_task(updatedTask, false, false);
	var row = gui_add_task(updatedTask, false);
	
    if(isAnimated){
        $(row).addClass("tsk_row_updated"); // intentionally in a different row
        $(row).animateToClass("tsk_row", 4000, function(){
                 // clean up (hidden left from template, 'style' is full of animation garbage)
                 $(this).removeClass("tsk_row_updated").attr("style", "");
         });
    }
}

/*
 * Adds a task row to the task list gui, in the right order (keeping it sorted),
 * if it passes the view filter,
 * Returns the new row.
 */
function gui_add_task(task, isAnimated)
 {
	// Make sure task passes view filter
	if (!taskFilter(task))
		return;

	// Create a new row for the task
	var newRow = create_row(task);

	// Add to visible_tasks array
	visible_tasks.push(task);

	//visible_tasks = visible_tasks.sort(taskComparer);
  visible_tasks = merge_sort(visible_tasks, taskComparer); // TEMPFIX
	var idx = indexOfTask(task, visible_tasks);

	// Add to document in the right place (the list is sorted)
	if (idx == 0)
		$('.tsk_list').prepend(newRow);
	else {
		var prevRow = $(".tsk_list .tsk_row")[idx - 1];
		$(prevRow).after(newRow);
	}
        
    // Animate if isAnimated flag is on
	if (isAnimated) {
		$(newRow).addClass("tsk_row_new"); // intentionally in a different row
		$(newRow).show();
		$(newRow).animateToClass("tsk_row", 4000, function() {
				// clean up (hidden left from template, 'style' is full of animation
				// garbage)
				$(this).removeClass("hidden tsk_row_new").attr("style", "");
			});
		adjustGadgetHeight();
	} else {
		$(newRow).removeClass("hidden");
	}
	
	return newRow;
}

/*
 * Animates task row removal from list. isCompleted flag might change the
 * animation type
 */
function gui_remove_task(task, isCompleted, isAnimated)
 {
	var animateToStyle = isCompleted ? "tsk_row_completed" : "tsk_row_deleted";

	var row = findVisibleRow(task);
	if (!row)
		return; // not visible

	// Close related task details view (if open)
	closeOpenTaskDetailsView(task.id);
	
	row.totalRemove = function() {

		// Remove from DOM when finished
		$(this).remove();

		// Remove from visible_tasks list
		var idx = indexOfTask(task, visible_tasks);
		if (idx != -1) // still there
			visible_tasks.splice(idx, 1);
		
	};

	if (isAnimated){
		$(row).find('.cell_title').addClass('line_througe_text');
		$(row).animate({ color: "black" }, 5000).animateToClass(animateToStyle, 2000, function(){
			this.totalRemove();
			adjustGadgetHeight();
		}); //.slideUp(700, totalRemove);
	} else {
		//row.totalRemove = totalRemove;
		row.totalRemove();
	}
}

// process GUI 'More Actions' menu
function moreActions(act) {
	if (act == '---') return;
	var r = 0;
	
	if (act == 'show-all') { setFilter({}); r = 1; }
	else if (act == 'show-active') { setFilter({is:'active'}); r = 1; }
	else if (act == 'show-overdue') { setFilter({is:'overdue'}); r = 1; }
	else if (act == 'show-completed') { setFilter({is:'completed'}); r = 1; }
	if (r) {
		selected_tasks = [];
		return;
	}
	
	if (act == 'postpone') postponeTasks(selected_tasks);
	else if (act == 'complete') completeTasks(selected_tasks);
	else if (act == 'uncomplete') uncompleteTasks(selected_tasks);
	else if (act == 'delete') deleteTasks(selected_tasks);
	else if (act == 'duplicate') duplicateTasks(selected_tasks);
	else if (act == 'call-dibs') delegateTasks(selected_tasks, wave.getViewer().getId());
	else if (act == 'prio-up') prioritizeTasks(selected_tasks, '--');
	else if (act == 'prio-down') prioritizeTasks(selected_tasks, '++');
	else if (act == 'prio1') prioritizeTasks(selected_tasks, 1);
	else if (act == 'prio2') prioritizeTasks(selected_tasks, 2);
	else if (act == 'prio3') prioritizeTasks(selected_tasks, 3);
	else if (act == 'prio4') prioritizeTasks(selected_tasks, 4);
	else if (act == 'assign-owner') {
		if (selected_tasks.length > 0) {
			var t = selected_tasks[0];
			if (t.owner.length < 1) t.owner = '(Assign Owner)';
			var dv = $('#tsk_details_' + t.id);
			if (dv.length > 0) taskDetailsViewRefresh(dv[0], t);
			else $('.tsk_row#' + t.id + ' .cell_edit_icon').click();
			$('#tsk_details_' + t.id + ' .downer').click();
		}
	}

	selected_tasks = [];
	gui_update_selections();
	$('#more-actions')[0].selectedIndex = 0;
}

function adjustGadgetHeight() {

	if (typeof gadgets == "undefined") // is inside gadget container?
		return;
	
	gadgets.window.adjustHeight();
}
